package com.example.demo.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.annotation.OptimisticRetry;
import com.example.demo.entity.Order;
import com.example.demo.feign.AccountService;
import com.example.demo.mapper.OrderMapper;
import com.example.demo.service.OrderService;
import com.example.demo.web.AjaxResult;
import io.seata.core.context.RootContext;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.stereotype.Service;

import java.util.Random;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    /**
     * 状态码
     */
    public static final String CODE_TAG = "code";

    /**
     * 返回内容
     */
    public static final String MSG_TAG = "msg";

    private static final String SUCCESS = "SUCCESS";

    private static final String FAIL = "FAIL";

    private static final String USER_ID = "U100001";

    private static final String COMMODITY_CODE = "C00321";

    @Autowired
    private AccountService accountService;

    @Override
    @SneakyThrows
    @OptimisticRetry(value = 10, maxExecuteTime = 3 * 1000)
    public int tryOptimisticLocking() {
        // 模拟耗时
        int quantity = RandomUtil.randomInt(1, 5);
        TimeUnit.SECONDS.sleep(quantity);
        // 库存是否充足判断等条件判断
        if (!(quantity > 3)) {
            throw new OptimisticLockingFailureException("decrease stock remaining amount optimistic locking failure!");
        }
        return quantity;
    }

    @Override
    public AjaxResult order(String userId, String commodityCode, int orderCount) {
        log.info("Order Service Begin ... xid: " + RootContext.getXID());

        int orderMoney = calculate(commodityCode, orderCount);

        invokerAccountService(orderMoney);

        final Order order = new Order();
        order.setId(IdUtil.getSnowflakeNextId());
        order.setUserId(userId);
        order.setCommodityCode(commodityCode);
        order.setCount(orderCount);
        order.setMoney(orderMoney);

        boolean save = this.save(order);

        if (RandomUtil.randomBoolean()) {
            throw new RuntimeException("Order Service this is a mock Exception");
        }

        log.info("Order Service End ... Created " + order);

        if (save) {
            return AjaxResult.success();
        }
        return AjaxResult.error();
    }

    private int calculate(String commodityId, int orderCount) {
        return RandomUtil.randomInt(1, 5) * orderCount;
    }

    private void invokerAccountService(int orderMoney) {
        AjaxResult result = accountService.account(USER_ID, orderMoney);
        if (!result.get(CODE_TAG).equals(HttpStatus.HTTP_OK)) {
            throw new RuntimeException(result.get(MSG_TAG).toString());
        }
    }

}
